function MyVue(options = {}){
  // 将所有的属性挂载到$option上
  this.$options = options;
  // 获取到data数据（Module）
  var data = this._data = this.$options.data;
  // 劫持数据
  observe(data)

  // this 就代理数据 this._data
  for (const key in data) {
    Object.defineProperty(this, key, {
      enumerable: true,
      get() { // this.a 这里取值的时候 实际上是去_data中的值
        return this._data[key] 
      },
      set(newVal) { 
        // 设置值的时候其实也是去改this._data.a的值
        this._data[key] = newVal
      }
    })
  }
}

// 给需要观察的对象都添加 Object.defineproperty() 的监听
function Observe(data){
  let dep = new Dep();
  for (let key in data) {
    let val = data[key]; // 递归 =》来实现深层的数据监听
    observe(val);
    Object.defineProperty(data, key, {
      enumerable: true,
      get() {
        /* 获取值的时候 Dep.target
          对于着 watcher的实例，把他创建的实例加到订阅队列中*/
        Dep.target && dep.addSub(Dep.target);
        return val
      },
      set(newval) {
        if (val === newval) { //设置的值是否和以前是一样的，如果是就什么都不做
          return
        }
        val = newval // 这里要把新设置的值也在添加一次数据劫持来实现深度响应,
        observe(newval);

        // 设置值的时候让所有的watcher.update方法执行即可触发所有数据更新
        dep.notify() 
      }
    })
   }
}

function observe(data) {
  // 这里做一下数据类型的判断，只有引用数据类型才去做数据劫持
  if (typeof data != 'object') return
  return new Observe(data)
}

// 发布订阅
function Dep() {    
  this.subs = []
}

Dep.prototype = {
  // 订阅
  addSubs: function(sub) {
    this.subs.push(sub);
  },
  // 通知
  notify: function() {
    this.subs.forEach(item => {
      item.update();
    })
  }
}

// watcher是一个类，通过这个类创建的函数都会有update的方法
function Watcher(fn) {
  this.fn = fn;
  // 这里我们新增了一些内容，用来可以获取对于的数据
  this.vm = vm;
  this.exp = exp; 
  Dep.target = this
  let val = vm;
  let arr = exp.split('.');
  /* 执行这一步的时候操作的是vm.a，
  而这一步操作其实就是操作的vm._data.a的操作
  会触发this代理的数据和_data上面的数据*/
  arr.forEach(function (k) {
    val = val[k]
  })
  Dep.target = null;
}

Watcher.prototype = {
  update: function() {
    let val = this.vm;
    let arr = this.exp.split('.');
    arr.forEach(function (k) {
      val = val[k]
    })
    this.fn(val) // 传一个新的值
  }
}